use super::DumpResult;
use aes_gcm::aead::{Aead, NewAead};
use aes_gcm::{Aes256Gcm, Key, Nonce};

/// Aes256Gcm加密工具
pub trait Aes256GcmEncryptUtil {
    /// Additional parameter types for decryption function,
    /// If you do not need additional encryption parameters, please set to type ().
    /// 
    /// 解密函数的额外参数类型,
    /// 如果不需要额外的加密参数，请设置为()类型.
    type CryptArgType;

    /// get encryption key and salt
    /// 
    /// * extra : Additional encryption parameters
    /// 
    /// 获取加密密钥和盐
    ///
    /// # 返回
    /// (密钥,盐)
    fn get_aes_key_nonce(extra:Option<Self::CryptArgType>) -> (String, String);
    
    /// Encryption function
    /// 
    /// # Parameters
    /// 
    /// * data plain data
    /// * extra : Additional encryption parameters
    /// 
    /// # Return
    /// 
    /// Encrypted data
    /// 
    /// 加密函数
    ///
    /// # 参数说明
    ///
    /// * data 待加密的明文数据
    ///
    /// # 返回
    /// 
    /// 加密后的密文
    fn encrypt(data: &str,extra:Option<Self::CryptArgType>) -> DumpResult<Vec<u8>> {
        let (key_str, nonce_str) = Self::get_aes_key_nonce(extra);
        let key = Key::from_slice(key_str.as_bytes());
        let cipher = Aes256Gcm::new(key);
        let nonce = Nonce::from_slice(nonce_str.as_bytes());
        let cipher_data = cipher.encrypt(nonce, data.as_bytes().as_ref()).unwrap();
        Ok(cipher_data)
    }

    /// Decryption function
    /// 
    /// # Parameters
    /// 
    /// * data Encrypted data to be decrypted
    /// * extra : Additional encryption parameters
    /// 
    /// # Return
    /// 
    /// Decrypted plain data
    /// 
    /// 解密函数
    ///
    /// # 参数说明
    ///
    /// * data 待解密的加密数据
    ///
    /// # 返回
    /// 解密后的明文
    fn decrypt(data: Vec<u8>,extra:Option<Self::CryptArgType>) -> DumpResult<Vec<u8>> {
        let (key_str, nonce_str) = Self::get_aes_key_nonce(extra);
        let key = Key::from_slice(key_str.as_bytes());
        let cipher = Aes256Gcm::new(key);
        let nonce = Nonce::from_slice(nonce_str.as_bytes());
        let plain_data = cipher.decrypt(nonce, data.as_ref()).unwrap();
        Ok(plain_data)
    }
}
